package com.example.demo.tool;/**
 * @Auter 李孝在
 * @Date 2025/1/18
 */

/**
 * @projectName: demo
 * @package: com.example.demo.tool
 * @className: FileDownTool
 * @author: Eric
 * @description: TODO
 * @date: 2025/1/18 03:43
 * @version: 1.0
 */
import cn.hutool.core.io.file.PathUtil;
import jakarta.activation.MimetypesFileTypeMap;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.zip.Zip64Mode;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @Author: LiHuaZhi
 * @Date: 2022/1/19 13:54
 * @Description:
 **/
public class FileDownTool {
    //??
    private static MimetypesFileTypeMap mimetypesFileTypeMap;

    /**
     * 下载http文件流
     */
//    public static void downloadHttpFile(String urlStr, HttpServletRequest request, HttpServletResponse response, String fileName) {
//        ServletOutputStream out = null;
//        InputStream inputStream = null;
//        try {
//            URL url = new URL(urlStr);
//            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//            //得到输入流
//            inputStream = conn.getInputStream();
//            //获取自己数组
//            byte[] getData = FileDownTool.inputStreamToByte(inputStream);
//            // 下载
//            out = response.getOutputStream();
//            long contentLength = getData.length;
//            //设置请求体参数
//            FileDownTool.setResponse(fileName, contentLength, request, response);
//            //写入内容
//            out.write(getData);
//            out.flush();
//        } catch (Exception e) {
//            throw new RuntimeException("下载失败!");
//        } finally {
//            try {
//                if (out != null) {
//                    out.close();
//                }
//                if (inputStream != null) {
//                    inputStream.close();
//                }
//            } catch (IOException e) {
//                throw new RuntimeException("数据流关闭失败!");
//            }
//        }
//    }


    //File、FileInputStream 转换为byte数组
    private static byte[] inputStreamToByte(InputStream inputStream) {
       //简而言之是设置缓冲区到内容中
        try {
            byte[] buffer = new byte[1024];
            int len = 0;
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            while ((len = inputStream.read(buffer)) != -1) {
                bos.write(buffer, 0, len);
            }
            bos.close();
            return bos.toByteArray();
        } catch (Exception e) {
            throw new RuntimeException("文件转换失败!");
        }
    }

    /**
     * 下载文件流
     */
    public static void downloadFile(File file, HttpServletRequest request, HttpServletResponse response, String fileName) {
        // 文件合法性判断
        if (file == null || !file.exists() || file.length() == 0L) {
            throw new RuntimeException("文件为空或不存在！");
        }

        try (RandomAccessFile randomFile = new RandomAccessFile(file, "r");
             FileChannel fileChannel = randomFile.getChannel();
             ServletOutputStream out = response.getOutputStream();
             WritableByteChannel outChannel = Channels.newChannel(out)) {

            // 获取文件长度
            long contentLength = file.length();

            // 获取请求头信息
            String range = request.getHeader("Range");
            long start = 0L;
            long end = contentLength - 1L;

            if (range != null && range.startsWith("bytes=")) {
                String[] values = range.split("=")[1].split("-");
                start = Long.parseLong(values[0]);
                if (values.length > 1 && !values[1].isEmpty()) {
                    end = Long.parseLong(values[1]);
                }
            }

            // 设置响应头
            FileDownTool.setResponse(fileName, contentLength, request, response);

            // 处理范围请求
            if (start > 0 || end < contentLength - 1) {
                response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
                response.setHeader("Content-Range", "bytes " + start + "-" + end + "/" + contentLength);
                contentLength = end - start + 1;
            }

            // 读取并发送文件内容
            fileChannel.transferTo(start, contentLength, outChannel);

            out.flush();
        } catch (IOException e) {
            throw new RuntimeException("文件下载失败: " + e.getMessage(), e);
        }
    }


    /**
     * 设置请求体
     */
    private static void setResponse(String fileName, long contentLength, HttpServletRequest request, HttpServletResponse response) {
        try {
            //获取MIME格式的数据,并设置响应体
            response.setContentType(FileDownTool.getContentType(fileName));
            boolean isPreview = "preview".equalsIgnoreCase(request.getParameter("source"));
            response.addHeader("Content-Disposition", (!isPreview ? "attachment; " : "") + "filename*=utf-8'zh_cn'" + URLEncoder.encode(fileName, StandardCharsets.UTF_8));
            response.setHeader("Accept-Ranges", "bytes");

            //范围请求（Range）:若是,则部分下载:用于分段下载的,否则全部下载
            String range = request.getHeader("Range");
            if (range == null) {
                response.setHeader("Content-Length", String.valueOf(contentLength));
            } else {
                response.setStatus(206);
                long requestStart = 0L;
                long requestEnd = 0L;
                String[] ranges = range.split("=");
                if (ranges.length > 1) {
                    String[] rangeDatas = ranges[1].split("-");
                    requestStart = Long.parseLong(rangeDatas[0]);
                    if (rangeDatas.length > 1) {
                        requestEnd = Long.parseLong(rangeDatas[1]);
                    }
                }

                long length = 0L;
                if (requestEnd > 0L) {
                    length = requestEnd - requestStart + 1L;
                    response.setHeader("Content-Length", String.valueOf(length));
                    response.setHeader("Content-Range", "bytes " + requestStart + "-" + requestEnd + "/" + contentLength);
                } else {
                    length = contentLength - requestStart;
                    response.setHeader("Content-Length", String.valueOf(length));
                    response.setHeader("Content-Range", "bytes " + requestStart + "-" + (contentLength - 1L) + "/" + contentLength);
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("response响应失败!");
        }
    }

    private static String getContentType(String fileName) {
        if (mimetypesFileTypeMap == null) {
            mimetypesFileTypeMap = new MimetypesFileTypeMap();
        }

        return mimetypesFileTypeMap.getContentType(fileName);
    }

    /**
     * 将磁盘的多个文件打包成压缩包并输出流下载

     */
    public static void zipDirFileToFile(String rootFilePath,List<File> zipPaths, HttpServletRequest request, HttpServletResponse response) {
        try {
            // 设置response参数并且获取ServletOutputStream
            ZipArchiveOutputStream zous = getServletOutputStream(response);
            for (File file : zipPaths) {
                InputStream inputStream = new FileInputStream(file);
                setByteArrayOutputStream(rootFilePath,file.toString(), inputStream, zous);
            }
            zous.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 将网络url资源文件的多个文件打包成压缩包并输出流下载
     */
//    public static void zipUrlToFile(String rootFilePath,List<String> ZipPaths, HttpServletRequest request, HttpServletResponse response) {
//        try {
//            // 设置response参数并且获取ServletOutputStream
//            ZipArchiveOutputStream zous = getServletOutputStream(response);
//            for (String url: ZipPaths) {
//                InputStream inputStream = getInputStreamFromUrl(url);
//                setByteArrayOutputStream(rootFilePath,url, inputStream, zous);
//            }
//            zous.close();
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//    }

    private static ZipArchiveOutputStream getServletOutputStream(HttpServletResponse response) throws Exception {

        String outputFileName = "文件" + new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + ".zip";
        response.reset();
        //默认
        response.setHeader("Content-Type", "application/octet-stream");
        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(outputFileName, "UTF-8"));
        response.setHeader("Pragma", "no-cache");
        response.setHeader("Cache-Control", "no-cache");
        ServletOutputStream out = response.getOutputStream();

        ZipArchiveOutputStream zous = new ZipArchiveOutputStream(out);
        zous.setUseZip64(Zip64Mode.AsNeeded);
        return zous;
    }

    private static void setByteArrayOutputStream(String rootFilePath,String zipFileStr, InputStream inputStream, ZipArchiveOutputStream zous) throws Exception {
        File zipFile=new File(zipFileStr);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len;
        while ((len = inputStream.read(buffer)) != -1) {
            baos.write(buffer, 0, len);
        }
        baos.flush();
        byte[] bytes = baos.toByteArray();

        //获取相对于根路径的相对路径
        String relativePath = zipFile.getAbsolutePath().substring(rootFilePath.length() + 1);
        //传到压缩包中
        ArchiveEntry entry = new ZipArchiveEntry(relativePath);
        zous.putArchiveEntry(entry);
        zous.write(bytes);
        zous.closeArchiveEntry();
        baos.close();
    }

    /**
     * 通过网络地址获取文件InputStream
     */
//    private static InputStream getInputStreamFromUrl(String path) {
//        URL url = null;
//        InputStream is = null;
//        try {
//            url = new URL(path);
//        } catch (MalformedURLException e) {
//            e.printStackTrace();
//        }
//        try {
//            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//            conn.setDoInput(true);
//            conn.connect();
//            is = conn.getInputStream();
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
//        return is;
//    }
}

